import hashlib
import logging
from typing import Any
from urllib.parse import urljoin

import aiohttp
from aiohttp.typedefs import StrOrURL

from tinkoff_bot.infrastructure.tinkoff_kassa.exceptions import TinkoffAPIError
from tinkoff_bot.infrastructure.tinkoff_kassa.models.payment import Payment

logger = logging.getLogger(__name__)


class TinkoffKassa:
    BASE_URL = "https://securepay.tinkoff.ru"

    def __init__(self, terminal_key: str, password: str):
        self.terminal_key = terminal_key
        self.password = password

    def calculate_token(self, payload: dict[Any, Any]) -> str:
        """Calculate the token for the request"""

        params = {k: v for k, v in payload.items() if not isinstance(v, (dict, list))}
        params["Password"] = self.password

        sorted_values = [str(value) for key, value in sorted(params.items())]
        values_string = "".join(sorted_values)

        return hashlib.sha256(values_string.encode("utf-8")).hexdigest()

    async def init_payment(self, amount: float, order_id: str) -> Payment:
        """
        Initialize payment
        @param amount: Amount in kopecks
        @param order_id: Order number
        """

        data = {"TerminalKey": self.terminal_key, "Amount": amount, "OrderId": order_id}
        data["Token"] = self.calculate_token(data)

        response = await self._make_request("POST", url=urljoin(self.BASE_URL, "Init"), data=data)
        return Payment(**response)

    async def get_payment_status(self, payment_id: int):
        """
        Returns the current payment status.

        @param payment_id (int): Payment identifier in the Tinkoff Kassa system.
        """

        print(self.terminal_key)
        data = {
            "TerminalKey": self.terminal_key,
            "PaymentId": payment_id,
        }
        data["Token"] = self.calculate_token(data)

        response = await self._make_request("POST", url=urljoin(self.BASE_URL, "GetState"), data=data)
        return Payment(**response)

    async def _make_request(self, method: str, url: StrOrURL, **kwargs) -> dict:
        """
        Make a request.
        @param method: HTTP Method
        @param url: endpoint link
        @param kwargs: data, params, json and other...
        @return: status and result or exception
        """

        async with aiohttp.ClientSession() as session:
            async with session.request(method, url, **kwargs) as response:
                response = await response.json(content_type="application/json")
        return self._validate_response(response, kwargs.get("data"))

    @staticmethod
    def _validate_response(response: dict, request_data: dict[str, Any] | None = None) -> dict:
        """Validate response"""

        if not response.get("Success"):
            logger.info(f"Tinkoff request data: {request_data}")
            logger.info(f"Tinkoff response: {response}")
            raise TinkoffAPIError(
                code=response.get("ErrorCode"), name=response.get("Message"), description=response.get("Details")
            )
        return response
